using System;
using System.Collections.Generic;
using System.Linq;
using Urs.Core;
using Urs.Core.Caching;
using Urs.Core.Data;
using Urs.Data.Domain.Users;
using Urs.Data.Domain.Security;
using Urs.Services.Users;

namespace Urs.Services.Security
{
    public partial class PermissionService : IPermissionService
    {
        #region Constants
        private const string PERMISSIONS_ALLOWED_KEY = "Urs.permission.allowed-{0}-{1}";
        private const string PERMISSIONS_ALLBYCUSTOMERROLE = "Urs.permission.allbyuserroleid-{0}";
        private const string PERMISSIONS_PATTERN_KEY = "Urs.permission.";
        #endregion

        #region Fields

        private readonly IRepository<PermissionRecord> _permissionPecordRepository;
        private readonly IRepository<PermissionRecordUserMapping> _permissionRecordUserMappingRepository;
        private readonly IUserService _userService;
        private readonly IWorkContext _workContext;
        private readonly ICacheManager _cacheManager;

        #endregion

        #region Ctor

        public PermissionService(IRepository<PermissionRecord> permissionPecordRepository,
            IRepository<PermissionRecordUserMapping> permissionRecordUserMappingRepository,
            IUserService userService,
            IWorkContext workContext, ICacheManager cacheManager)
        {
            this._permissionPecordRepository = permissionPecordRepository;
            this._permissionRecordUserMappingRepository = permissionRecordUserMappingRepository;
            this._userService = userService;
            this._workContext = workContext;
            this._cacheManager = cacheManager;
        }

        #endregion

        #region Utilities

        protected virtual IList<PermissionRecord> GetPermissionRecordsByUserRoleId(int userRoleId)
        {
            var key = string.Format(PERMISSIONS_ALLBYCUSTOMERROLE, userRoleId);
            return _cacheManager.Get(key, () =>
            {
                var query = from pr in _permissionPecordRepository.Table
                            join prcrm in _permissionRecordUserMappingRepository.Table on pr.Id equals prcrm.PermissionRecordId
                            where prcrm.UserRoleId == userRoleId
                            orderby pr.Id
                            select pr;

                return query.ToList();
            });
        }


        protected virtual bool Authorize(string permissionRecordSystemName, UserRole userRole)
        {
            if (String.IsNullOrEmpty(permissionRecordSystemName))
                return false;

            string key = string.Format(PERMISSIONS_ALLOWED_KEY, userRole.Id, permissionRecordSystemName);
            return _cacheManager.Get(key, () =>
            {
                var permissions = GetPermissionRecordsByUserRoleId(userRole.Id);
                foreach (var permission1 in permissions)
                    if (permission1.SystemName.Equals(permissionRecordSystemName, StringComparison.InvariantCultureIgnoreCase))
                        return true;

                return false;
            });
        }

        #endregion

        #region Methods

        public virtual void DeletePermissionRecord(PermissionRecord permission)
        {
            if (permission == null)
                throw new ArgumentNullException("permission");

            _permissionPecordRepository.Delete(permission);

            _cacheManager.RemoveByPattern(PERMISSIONS_PATTERN_KEY);
        }

        public virtual PermissionRecord GetPermissionRecordById(int permissionId)
        {
            if (permissionId == 0)
                return null;

            return _permissionPecordRepository.GetById(permissionId);
        }

        public virtual PermissionRecord GetPermissionRecordBySystemName(string systemName)
        {
            if (String.IsNullOrWhiteSpace(systemName))
                return null;

            var query = from pr in _permissionPecordRepository.Table
                        where pr.SystemName == systemName
                        orderby pr.Id
                        select pr;

            var permissionRecord = query.FirstOrDefault();
            return permissionRecord;
        }

        public virtual IList<PermissionRecord> GetAllPermissionRecords(string group = "")
        {
            var query = from pr in _permissionPecordRepository.Table
                        orderby pr.Group
                        select pr;

            var permissions = query.ToList();
            if (!string.IsNullOrEmpty(group))
                permissions = permissions.Where(q => q.Group == group).ToList();
            return permissions;
        }

        public virtual IList<string> GetAllGroup()
        {
            var query = from pr in _permissionPecordRepository.Table
                        group pr by pr.Group into g
                        select g.Key;
            var permissions = query.ToList();
            return permissions;
        }

        public virtual void InsertPermissionRecord(PermissionRecord permission)
        {
            if (permission == null)
                throw new ArgumentNullException("permission");

            _permissionPecordRepository.Insert(permission);

            _cacheManager.RemoveByPattern(PERMISSIONS_PATTERN_KEY);
        }

        public virtual void UpdatePermissionRecord(PermissionRecord permission)
        {
            if (permission == null)
                throw new ArgumentNullException("permission");

            _permissionPecordRepository.Update(permission);

            _cacheManager.RemoveByPattern(PERMISSIONS_PATTERN_KEY);
        }

        public virtual bool Authorize(PermissionRecord permission)
        {
            return Authorize(permission, _workContext.CurrentUser);
        }

        public virtual bool Authorize(PermissionRecord permission, User user)
        {
            if (permission == null)
                return false;

            if (user == null)
                return false;



            return Authorize(permission.SystemName, user);
        }

        public virtual bool Authorize(string permissionRecordSystemName)
        {
            return Authorize(permissionRecordSystemName, _workContext.CurrentUser);
        }

        public virtual bool Authorize(string permissionRecordSystemName, User user)
        {
            if (String.IsNullOrEmpty(permissionRecordSystemName))
                return false;

            var userRoles = user.UserRoles.Where(cr => cr.Active);
            foreach (var role in userRoles)
                if (Authorize(permissionRecordSystemName, role))
                    return true;

            return false;
        }

        public virtual IList<PermissionRecordUserMapping> GetPermissionRecordUserMappings()
        {
            var query = from pr in _permissionRecordUserMappingRepository.Table
                        orderby pr.Id
                        select pr;

            var permissions = query.ToList();
            return permissions;
        }

        public virtual IList<PermissionRecordUserMapping> GetPermissionRecordUserMappings(int userRoleId)
        {

            var query = from pr in _permissionRecordUserMappingRepository.Table
                        where pr.UserRoleId == userRoleId
                        orderby pr.Id
                        select pr;

            var permissions = query.ToList();
            return permissions;
        }
        #endregion
    }
}